import java.math.BigInteger;

/**
 * The Client's buffer. Acts as the client's input/output stream.
 * 
 * @author Jagex
 */
public final class Packet extends NodeSub {
	private static int pooledPointers;
	private static final int[] bitMasks = { 0, 1, 3, 7, 15, 31, 63, 127, 255, 511, 1023, 2047,
	4095, 8191, 16383, 32767, 65535, 0x1ffff, 0x3ffff, 0x7ffff, 0xfffff, 0x1fffff, 0x3fffff,
	0x7fffff, 0xffffff, 0x1ffffff, 0x3ffffff, 0x7ffffff, 0xfffffff, 0x1fffffff, 0x3fffffff,
	0x7fffffff, -1 };
	private static final Deque deque = new Deque();
	public int bitOffset;
	public byte buffer[];
	public int offset;
	public IsaacCipher packetCryption;

	public static Packet create() {
		synchronized (Packet.deque) {
			Packet buffer = null;
			if (Packet.pooledPointers > 0) {
				Packet.pooledPointers--;
				buffer = (Packet) Packet.deque.popFront();
			}
			if (buffer != null) {
				buffer.offset = 0;
				return buffer;
			}
		}
		Packet buffer = new Packet();
		buffer.offset = 0;
		buffer.buffer = new byte[5000];
		return buffer;
	}

	/**
	 * createFrame()
	 */
	public void putOperation(int op) {
		buffer[offset++] = (byte) (op + packetCryption.getNextKey());
	}

	/**
	 * doKeys()
	 */
	public void encodeRsa() {
		int off = offset;
		offset = 0;
		byte buf[] = new byte[off];
		getBytes(buf, off, 0);
		BigInteger bigInt1 = new BigInteger(buf);
		BigInteger bigInt2 = bigInt1;
		byte dest[] = bigInt2.toByteArray();
		offset = 0;
		putByte(dest.length);
		putBytes(dest, 0, dest.length);
	}

	public void finishBitAccess() {
		offset = (bitOffset + 7) / 8;
	}

	public void initBitAccess() {
		bitOffset = offset * 8;
	}

	/**
	 * method400(int i)
	 */
	public void putLEShort_duplicate(int val) {
		buffer[offset++] = (byte) val;
		buffer[offset++] = (byte) (val >> 8);
	}

	/**
	 * method403(int j)
	 */
	public void putLEInt(int val) {
		buffer[offset++] = (byte) val;
		buffer[offset++] = (byte) (val >> 8);
		buffer[offset++] = (byte) (val >> 16);
		buffer[offset++] = (byte) (val >> 24);
	}

	/**
	 * method421()
	 */
	public int getUnsignedSmart() {
		int i = buffer[offset] & 0xff;
		if (i < 128) {
			return getUnsignedByte() - 64;
		} else {
			return getUnsignedShort() - 49152;
		}
	}

	/**
	 * method422()
	 */
	public int getSmart() {
		int i = buffer[offset] & 0xff;
		if (i < 128) {
			return getUnsignedByte();
		} else {
			return getUnsignedShort() - 32768;
		}
	}

	/**
	 * method424(int i)
	 */
	public void putByteC(int val) {
		buffer[offset++] = (byte) (-val);
	}

	/**
	 * method425(int j)
	 */
	public void putByteS(int val) {
		buffer[offset++] = (byte) (128 - val);
	}

	/**
	 * method426()
	 */
	public int getUnsignedByteA() {
		return buffer[offset++] - 128 & 0xff;
	}

	/**
	 * method427()
	 */
	public int getUnsignedByteC() {
		return -buffer[offset++] & 0xff;
	}

	/**
	 * method428()
	 */
	public int getUnsignedByteS() {
		return 128 - buffer[offset++] & 0xff;
	}

	/**
	 * method429()
	 */
	public byte getByteC() {
		return (byte) (-buffer[offset++]);
	}

	/**
	 * method430()
	 */
	public byte getByteS() {
		return (byte) (128 - buffer[offset++]);
	}

	/**
	 * method431(int i)
	 */
	public void putLEShort(int val) {
		buffer[offset++] = (byte) val;
		buffer[offset++] = (byte) (val >> 8);
	}

	/**
	 * method432(int j)
	 */
	public void putShortA(int val) {
		buffer[offset++] = (byte) (val >> 8);
		buffer[offset++] = (byte) (val + 128);
	}

	/**
	 * method433(int j)
	 */
	public void putLEShortA(int val) {
		buffer[offset++] = (byte) (val + 128);
		buffer[offset++] = (byte) (val >> 8);
	}

	/**
	 * method434()
	 */
	public int getUnsignedLEShort() {
		offset += 2;
		return ((buffer[offset - 1] & 0xff) << 8) + (buffer[offset - 2] & 0xff);
	}

	/**
	 * method435()
	 */
	public int getUnsignedLEShortA() {
		offset += 2;
		return ((buffer[offset - 2] & 0xff) << 8) + (buffer[offset - 1] - 128 & 0xff);
	}

	/**
	 * getValue()
	 */
	public int getUnsignedShortA() {
		offset += 2;
		return ((buffer[offset - 1] & 0xff) << 8) + (buffer[offset - 2] - 128 & 0xff);
	}

	/**
	 * method437()
	 */
	public int getLEShort() {
		offset += 2;
		int j = ((buffer[offset - 1] & 0xff) << 8) + (buffer[offset - 2] & 0xff);
		if (j > 32767) {
			j -= 0x10000;
		}
		return j;
	}

	/**
	 * method438()
	 */
	public int getLEShortA() {
		offset += 2;
		int j = ((buffer[offset - 1] & 0xff) << 8) + (buffer[offset - 2] - 128 & 0xff);
		if (j > 32767) {
			j -= 0x10000;
		}
		return j;
	}

	/**
	 * method439()
	 */
	public int getInt2() {
		offset += 4;
		return ((buffer[offset - 2] & 0xff) << 24) + ((buffer[offset - 1] & 0xff) << 16)
		+ ((buffer[offset - 4] & 0xff) << 8) + (buffer[offset - 3] & 0xff);
	}

	/**
	 * method440()
	 */
	public int getInt1() {
		offset += 4;
		return ((buffer[offset - 3] & 0xff) << 24) + ((buffer[offset - 4] & 0xff) << 16)
		+ ((buffer[offset - 1] & 0xff) << 8) + (buffer[offset - 2] & 0xff);
	}

	/**
	 * method441()
	 */
	public void getReverseBytesA(byte buf[], int len, int off) {
		for (int i = (len + off) - 1; i >= len; i--) {
			buffer[offset++] = (byte) (buf[i] + 128);
		}
	}

	/**
	 * method442()
	 */
	public void getReverseBytes(byte buf[], int len, int off) {
		for (int k = (len + off) - 1; k >= len; k--) {
			buf[k] = buffer[this.offset++];
		}
	}

	/**
	 * read3Bytes
	 */
	public int get24BitInt() {
		offset += 3;
		return ((buffer[offset - 3] & 0xff) << 16) + ((buffer[offset - 2] & 0xff) << 8)
		+ (buffer[offset - 1] & 0xff);
	}

	public int getBits(int bitOff) {
		int off = bitOffset >> 3;
		int max_bit_len = 8 - (bitOffset & 7);
		int dest = 0;
		bitOffset += bitOff;
		for (; bitOff > max_bit_len; max_bit_len = 8) {
			dest += (buffer[off++] & Packet.bitMasks[max_bit_len]) << bitOff - max_bit_len;
			bitOff -= max_bit_len;
		}
		if (bitOff == max_bit_len) {
			dest += buffer[off] & Packet.bitMasks[max_bit_len];
		} else {
			dest += buffer[off] >> max_bit_len - bitOff & Packet.bitMasks[bitOff];
		}
		return dest;
	}

	public byte[] getBytes() {
		int off = offset;
		while (buffer[offset++] != 10) {
			;
		}
		byte dest[] = new byte[offset - off - 1];
		System.arraycopy(buffer, off, dest, off - off, offset - 1 - off);
		return dest;
	}

	public void getBytes(byte buf[], int len, int off) {
		for (int l = off; l < off + len; l++) {
			buf[l] = buffer[offset++];
		}
	}

	/**
	 * readDWord()
	 */
	public int getInt() {
		offset += 4;
		return ((buffer[offset - 4] & 0xff) << 24) + ((buffer[offset - 3] & 0xff) << 16)
		+ ((buffer[offset - 2] & 0xff) << 8) + (buffer[offset - 1] & 0xff);
	}

	/**
	 * readQWord()
	 */
	public long getLong() {
		long l1 = getInt() & 0xffffffffL;
		long l2 = getInt() & 0xffffffffL;
		return (l1 << 32) + l2;
	}

	public byte getByte() {
		return buffer[offset++];
	}

	/**
	 * readSignedWord()
	 */
	public int getShort() {
		offset += 2;
		int i = ((buffer[offset - 2] & 0xff) << 8) + (buffer[offset - 1] & 0xff);
		if (i > 32767) {
			i -= 0x10000;
		}
		return i;
	}

	/**
	 * readString()
	 */
	public String getString() {
		int i = offset;
		while (buffer[offset++] != 10) {
			;
		}
		return new String(buffer, i, offset - i - 1);
	}

	public int getUnsignedByte() {
		return buffer[offset++] & 0xff;
	}

	/**
	 * readUnsignedWord()
	 */
	public int getUnsignedShort() {
		offset += 2;
		return ((buffer[offset - 2] & 0xff) << 8) + (buffer[offset - 1] & 0xff);
	}

	public void putBytes(byte data[], int length, int offset) {
		for (int i = length; i < length + offset; i++) {
			buffer[this.offset++] = data[i];
		}
	}

	public void putSizeByte(int val) {
		buffer[offset - val - 1] = (byte) val;
	}

	/**
	 * writeInt(int i)
	 */
	public void putInt(int val) {
		buffer[offset++] = (byte) (val >> 24);
		buffer[offset++] = (byte) (val >> 16);
		buffer[offset++] = (byte) (val >> 8);
		buffer[offset++] = (byte) val;
	}

	/**
	 * writeDWordBigEndian(int i)
	 */
	public void put24BitInt(int val) {
		buffer[offset++] = (byte) (val >> 16);
		buffer[offset++] = (byte) (val >> 8);
		buffer[offset++] = (byte) val;
	}

	/**
	 * writeQWord(long l)
	 */
	public void putLong(long val) {
		try {
			buffer[offset++] = (byte) (int) (val >> 56);
			buffer[offset++] = (byte) (int) (val >> 48);
			buffer[offset++] = (byte) (int) (val >> 40);
			buffer[offset++] = (byte) (int) (val >> 32);
			buffer[offset++] = (byte) (int) (val >> 24);
			buffer[offset++] = (byte) (int) (val >> 16);
			buffer[offset++] = (byte) (int) (val >> 8);
			buffer[offset++] = (byte) (int) val;
		} catch (RuntimeException runtimeexception) {
			Signlink.reporterror("14395, " + 5 + ", " + val + ", " + runtimeexception.toString());
			throw new RuntimeException();
		}
	}

	/**
	 * writeString(String s)
	 */
	public void putString(String string) {
		System.arraycopy(string.getBytes(), 0, buffer, offset, string.length());
		offset += string.length();
		buffer[offset++] = 10;
	}

	/**
	 * writeWord(int i)
	 */
	public void putShort(int val) {
		buffer[offset++] = (byte) (val >> 8);
		buffer[offset++] = (byte) val;
	}

	/**
	 * writeWordBigEndian()
	 */
	public void putByte(int val) {
		buffer[offset++] = (byte) val;
	}

	private Packet() {
	}

	public Packet(byte buf[]) {
		buffer = buf;
		offset = 0;
	}
}
